#pragma once
#include "session.hpp"
#include "util.hpp"
#include "websocketpp/server.hpp"
#include "websocketpp/config/asio_no_tls.hpp"
#include <unordered_map>
#include <mutex>

#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1

using session_ptr = std::shared_ptr<session>; // 智能指针管理session对象

class session_manager
{
public:
    session_manager(wsserver_t *server)
        : _server(server), _ssid(1)
    {
        DBG_LOG("session_manager init success");
    }
    ~session_manager()
    {
        DBG_LOG("session_manager destroy");
    }

    session_ptr create_session(uint64_t uid, ss_status status)
    {
        // 防止多个线程同时获取相同的_ssid，也防止同时修改_session容器
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp(new session(_ssid));
        ssp->set_status(status);
        ssp->set_uid(uid);
        _session.insert(std::make_pair(_ssid, ssp));
        _ssid++;
        return ssp;
    }

    void append_session(const session_ptr &ssp)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->get_ssid(), ssp));
    }

    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.erase(ssid);
    }
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _session.find(ssid);
        if (it != _session.end())
        {
            return it->second;
        }
        return nullptr;
    }

    // 设置session的定时器
    void set_session_expire_time(uint64_t ssid, int ms)
    {
        // 依赖于websocketpp的定时器来完成session生命周期的管理
        // 登录之后，创建session，session需要在指定时间无通信后删除
        // 但是进入游戏大厅，或者游戏房间，这个session就应该永久存在
        // 等待退出游戏大厅或者游戏房间，这个session应该重新被设置为临时，长时间无通信后删除

        // 通过ssid获取对应容器中的session
        session_ptr ssp = get_session_by_ssid(ssid);
        if (!ssp.get())
        {
            // 如果session不存在，则返回
            return;
        }
        
        //session存在
        // 获取session关联的定时器tp
        wsserver_t::timer_ptr tp = ssp->get_timer();
        if (tp.get() == nullptr || ms == SESSION_FOREVER)
        {
            // 1.定时器为空或者设置为永久存在---无需设置定时器
            return;
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            // 2.当前无定时器并且会话需要设置超时  ---创建新的定时器任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,
            std::bind(&session_manager::remove_session, this, ssid));//bind，如果超时了，则调用remove
            ssp->set_timer(tmp_tp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            // 3.在session设置了定时删除的情况下，将session设置为永久存在
            tp->cancel(); // 取消定时任务，时间被记为已取消
            ssp->set_timer(wsserver_t::timer_ptr());//将会话对应的定时器设为nullptr，相当于解除会话与定时器的关联
            _server->set_timer(0,
            std::bind(&session_manager::append_session, this, ssp));//TODO
        }
        else if (tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            //重置定时时间
            tp->cancel();
            ssp->set_timer(wsserver_t::timer_ptr());
            _server->set_timer(0,
            std::bind(&session_manager::append_session, this, ssp));

            // 重新给session添加定时摧毁任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms,
            std::bind(&session_manager::remove_session, this, ssp->get_ssid()));

            ssp->set_timer(tmp_tp);
        }
    }

private:
    uint64_t _ssid; // 用于生成唯一的会话session id
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _session; // ssid -> session
    wsserver_t *_server;
};